package secloud

import (
	"context"
	"errors"
	"github.com/antihax/optional"
)

type CipherClient struct {
	Base *BaseClient
	ctx  context.Context
}

func NewCipherClient(ctx context.Context) *CipherClient {
	return &CipherClient{
		Base: BaseSvc,
		ctx:  ctx,
	}
}

type EncryptOpts struct {
	SessionId optional.String
	PlainText optional.String
	Algorithm optional.String
}

type DecryptOpts struct {
	SessionId  optional.String
	CipherText optional.String
	Algorithm  optional.String
}

type SignOpts struct {
	PlainText optional.String
	Algorithm optional.String
}

type VerifyOpts struct {
	WorkMode  optional.String
	Algorithm optional.String
	PublicKey optional.String
	PlainText optional.String
	Signature optional.String
}

type HmacSumOpts struct {
	KeyId optional.String
	Data  optional.String
}

type HmacCheckOpts struct {
	KeyId   optional.String
	Data    optional.String
	HmacSum optional.String
}

type RandomOpts struct {
	Length optional.Int
}

func (cc *CipherClient) Encrypt(opts *EncryptOpts) (*EncryptRespData, error) {
	if cc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret EncryptResp
	_, err := cc.Base.C.R().
		SetHeader("Authorization", "Bearer "+cc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"plain_text": opts.PlainText.Value(),
			"session_id": opts.SessionId.Value(),
			"algorithm":  opts.Algorithm.Value(),
		}).
		SetResult(&ret).
		Post("/cipher/encrypt")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (cc *CipherClient) Decrypt(opts *DecryptOpts) (*DecryptRespData, error) {
	if cc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret DecryptResp
	_, err := cc.Base.C.R().
		SetHeader("Authorization", "Bearer "+cc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"cipher_text": opts.CipherText.Value(),
			"session_id":  opts.SessionId.Value(),
			"algorithm":   opts.Algorithm.Value(),
		}).
		SetResult(&ret).
		Post("/cipher/decrypt")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (cc *CipherClient) Sign(opts *SignOpts) (*SignRespData, error) {
	if cc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret SignResp
	_, err := cc.Base.C.R().
		SetHeader("Authorization", "Bearer "+cc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"plain_text": opts.PlainText.Value(),
			"algorithm":  opts.Algorithm.Value(),
		}).
		SetResult(&ret).
		Post("/cipher/sign")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (cc *CipherClient) Verify(opts *VerifyOpts) (*VerifyRespData, error) {
	if cc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret VerifyResp
	_, err := cc.Base.C.R().
		SetHeader("Authorization", "Bearer "+cc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"work_mode":  opts.WorkMode.Value(),
			"algorithm":  opts.Algorithm.Value(),
			"public_key": opts.PublicKey.Value(),
			"plain_text": opts.PlainText.Value(),
			"signature":  opts.Signature.Value(),
		}).
		SetResult(&ret).
		Post("/cipher/verify")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}

func (cc *CipherClient) Random(opts *RandomOpts) (*RandomRespData, error) {
	if cc.ctx.Value(ContextAccessToken) == nil {
		return nil, errors.New("token为空！")
	}

	var ret RandomResp
	_, err := cc.Base.C.R().
		SetHeader("Authorization", "Bearer "+cc.ctx.Value(ContextAccessToken).(string)).
		SetBody(map[string]interface{}{
			"length": opts.Length.Value(),
		}).
		SetResult(&ret).
		Post("/cipher/random")
	if err != nil {
		return nil, err
	}

	if ret.Code != 200 {
		return nil, errors.New(ret.Msg)
	}

	return ret.Data, nil
}
